package view;

import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.input.Mouse;
import com.threed.jpct.Camera;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;
import com.threed.jpct.util.KeyMapper;
import com.threed.jpct.util.KeyState;

import config.GameConfig;

public class SpaceCamera2 {	
	private Camera camera;
	private List<Object3D> sceneObjects;
	private KeyMapper keyMapper;
	private GameConfig config;
	private float distance;
	private boolean rotateMode;
	
	private boolean PAGE_UP_PRESSED;
	private boolean PAGE_DOWN_PRESSED;
	private boolean UP_PRESSED;
	private boolean DOWN_PRESSED;
	private boolean LEFT_PRESSED;
	private boolean RIGHT_PRESSED;

	public SpaceCamera2(World world, GameConfig config){
		setCamera(world.getCamera());
		setDefaultScene();	
		
		
		keyMapper = new KeyMapper();
		this.config = config;
		distance = config.getDefaultCameraDist();
		setDefaultPosition();
		lookAtCenter();
	}
	
	private void lookAtCenter(){
		camera.lookAt(new SimpleVector(0,0,0));
	}
	
	private void setDefaultScene(){
		this.sceneObjects = new ArrayList<Object3D>();
	}
	
	private void setDefaultPosition(){
		camera.setPosition(new SimpleVector(0, 0, distance));
	}
	
	/**
	 * Updates camera position by using mouse and keyboard
	 */
	public void update(){
		handleKeys();
		zoom();
		rotate();	
	}
	
	/**
	 * Rotates camera 
	 */
	public void rotate(){
		/*Checking if right mouse button is clicked and held*/
		if(rotateMode && Mouse.isButtonDown(1)){
			int dx = Mouse.getDX();
			int dy = Mouse.getDY();
			rotateSceneObjects(-dx,dy);
		}
		/*Checking if right mouse button was held and now released*/
		else if(rotateMode && !Mouse.isButtonDown(1)){
			rotateMode = false;
		}
		/*Checking if right mouse button is just clicked but not held*/
		else if(!rotateMode && Mouse.isButtonDown(1)){
			rotateMode = true;
		}
		/*Checking if some of arrow buttons is pushed*/
		if(RIGHT_PRESSED){
			rotateSceneObjects(-config.getDefaultRotateDx(), 0);
		}
		if(LEFT_PRESSED){
			rotateSceneObjects(config.getDefaultRotateDx(), 0);
		}	
		if(DOWN_PRESSED){
			rotateSceneObjects(0, -config.getDefaultRotateDy());
		}
		if(UP_PRESSED){
			rotateSceneObjects(0, config.getDefaultRotateDy());
		}		
	}
	
	private boolean checkDistanceLimits(float distance, float zoom){
		float newDistance = distance - zoom;
		if((Math.abs(newDistance) <= config.getMaxZoom() && Math.abs(newDistance) >= config.getMinZoom())){
			distance = newDistance;
			return true;
		}	
		return false;
	}
	
	public void zoom(){
		float zoom = Mouse.getDWheel()/100*config.getDefaultZoomDist();
		if(checkDistanceLimits(distance, zoom)){
			distance -= zoom;
		}	
		setDefaultPosition();
		if(PAGE_DOWN_PRESSED && checkDistanceLimits(distance, config.getDefaultZoomDist())){
			distance += config.getDefaultZoomDist();
		}
		if(PAGE_UP_PRESSED && checkDistanceLimits(distance, config.getDefaultZoomDist())){
			distance -= config.getDefaultZoomDist();
		}	
		
	}

	/**
	 * Checking if some keys were pressed or released
	 */
	public void handleKeys(){
		KeyState state = null;
		while((state = keyMapper.poll()) != KeyState.NONE){
			int code = state.getKeyCode();
			if(code == KeyEvent.VK_DOWN){
				DOWN_PRESSED = state.getState();
			}
			else if(code == KeyEvent.VK_UP){
				UP_PRESSED = state.getState();
			}	
			else if(code == KeyEvent.VK_LEFT){
				LEFT_PRESSED = state.getState();
			}
			else if(code == KeyEvent.VK_RIGHT){
				RIGHT_PRESSED = state.getState();
			}	
			else if(code == KeyEvent.VK_PAGE_DOWN){
				PAGE_DOWN_PRESSED = state.getState();
			}
			else if(code == KeyEvent.VK_PAGE_UP){
				PAGE_UP_PRESSED = state.getState();
			}		
		}		
	}
	
	/**
	 * Rotates all objects added to scene
	 * @param dx - it is change of horizontal position in 2D
	 * @param dy - it is change of vertical position in 2D
	 */
	public void rotateSceneObjects(int dx, int dy){
		for(int i=0; i<sceneObjects.size(); i++){
			sceneObjects.get(i).rotateX(config.getAnglePerPixel()*dy);
			sceneObjects.get(i).rotateY(config.getAnglePerPixel()*dx);
		}	
	}
	
	public void addObjectToScene(Object3D object3D){
		this.sceneObjects.add(object3D);
	}
	
	public Camera getCamera() {
		return camera;
	}

	public void setCamera(Camera camera) {
		this.camera = camera;
	}

	public List<Object3D> getSceneObjects() {
		return sceneObjects;
	}

	public void setSceneObjects(List<Object3D> sceneObjects) {
		this.sceneObjects = sceneObjects;
	}

	public float getDistance() {
		return distance;
	}

	public void setDistance(int distance) {
		this.distance = distance;
	}
}
